/*
 * Open LiveReplay Server - Live replay server for Supreme Commander
 * 
 * Copyright (C) 2009, 2009 Open LiveReplay Server Project
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details:
 * http://www.gnu.org/copyleft/lesser.html#TOC3
 */
package net.galacticwar.replayserver;

import java.io.ByteArrayOutputStream;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;

/**
 * 
 * This class handle all incoming requests coming from clients
 * 
 * @author Yoann Meste (aka Mancer)
 *
 */
public class ReplayServerHandler extends IoHandlerAdapter{
	private static final Logger logger = Logger.getLogger("ReplayServer");

	private final Map<String, LiveReplay> replays;

	public ReplayServerHandler(Map<String, LiveReplay> replays) {
		this.replays = replays;
	}

	@Override
	public void messageReceived(IoSession session, Object message)
	throws Exception {
		ByteBuffer buffer = (ByteBuffer)message;
		
		Object o = session.getAttribute("REPLAY");
		if (o==null) {
			// the command identifier is always stored in the first integer
			int char1 = buffer.get();
			int char2 = buffer.get();
			
			if (char1 == 80 && char2 == 47) {
				//This session wants to send a new replay...
				String replayName = readString(buffer);
				logger.log(Level.INFO, "New replay received {0} from {1}", new Object[]{replayName , session.getRemoteAddress()});

				
				//Check if the replay name is valid
				if (replayName.endsWith(".SCFAReplay")==false) {
					logger.log(Level.WARNING, "The replay name {0} is not valid", replayName);
					session.close();
					return;
				}
				
				// Check if the replay name already exists
				LiveReplay oldReplay = replays.get(replayName);
				if (oldReplay != null) {
					// A previous replay with same name name has been found
					
					if (oldReplay.isInProgress()) {
						// We do not overwrite the previous replay if it is still in progress 
						session.close();
						return;
						
					}
				}
				
				LiveReplay replay = new LiveReplay(replayName, session);
				replays.put(replayName, replay);
				
				int count = buffer.remaining();
				if (count !=0) {
					byte[] data = new byte[count];
					buffer.get(data);

					replay.addData(data);
				}
			}
			else if(char1 == 71 && char2 == 47)  {
				//This session requests a replay
				String replayName = readString(buffer);
				logger.log(Level.INFO, "The session {0} requests the replay {1}", new Object[]{session.getRemoteAddress(), replayName});
				
				LiveReplay replay = replays.get(replayName);
				
				if (replay == null) {
					logger.log(Level.WARNING, "The session {0} requests an unknown replay {1}", new Object[]{session.getRemoteAddress(), replayName});

					session.close();
					return;
				}
				replay.addListener(session);
			}
		}
		else{
			//This session is already registered as a sender
			// ( the listeners does not send anymore bytes )
			LiveReplay replay = (LiveReplay)o;
			int count = buffer.remaining();
			byte[] data = new byte[count];
			buffer.get(data);

			replay.addData(data);
		}
	}

	
	@Override
	public void sessionClosed(IoSession session) throws Exception {
		Object o = session.getAttribute("REPLAY");
		
		if (o!=null) {
			// If this session was registered in a replay, we remove it.
			logger.log(Level.INFO, "The session {0} has closed", session.getRemoteAddress());
			LiveReplay replay = (LiveReplay)o;
			replay.removeListener(session);
		}
	}
	
	@Override
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		logger.log(Level.WARNING, "Error !", cause);
	}

	/**
	 * Read a {@code String} terminated by a null character
	 * 
	 * @param buffer the {@code ByteBuffer} to be read
	 * @return A {@code String} read in the buffer
	 * 
	 */
	private String readString(ByteBuffer buffer) throws Exception{
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte b;
		while ((b = buffer.get())!=0) {
			baos.write(b);
		}

		return new String(baos.toByteArray());
	}

}
